package geradordao;

import java.util.ArrayList;
import java.util.List;


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Iana
 */
public class ASRPGArvore {

    private List<Token> tokens;
    private List<String> erros = new ArrayList<String>();
    private Token token;
    private Integer pToken = 0;
    private No raiz;

    private void nextToken() {
        if (tokens.size() > pToken) {
            token = tokens.get(pToken);
            //System.out.println(token.getImagem() + " " + token.getClasse());
            pToken++;
        }
    }

    public No getRaiz() {
        return raiz;
    }

    public void setRaiz(No raiz) {
        this.raiz = raiz;
    }

    public ASRPGArvore(List<Token> tokens) {
        this.tokens = tokens;
    }

    public void analisar() {
        pToken = 0;
        nextToken();
        raiz = QueryList();
    }

    //<Query_List> ::= <Query> ';' <Query_List> |
    No QueryList() {
        No no = new No(TipoNo.No_QueryList);
        if (token.getImagem().equalsIgnoreCase("create") || token.getImagem().equalsIgnoreCase("alter")) {
            no.addFilho(Query());
            if (token.getImagem().equals(";")) {
                nextToken();
                no.addFilho(QueryList());
            } else {
                erros.add("Esperado na QueryList: ';' " + " Linha: " + token.getLinha());
            }
        }
        return no;
    }

    // <Query> ::= <Alter Stm> | <Create Stm>
    No Query() {
        No no = new No(TipoNo.No_Query);
        if (token.getImagem().equalsIgnoreCase("create")) {
            no.addFilho(CreateStm());
        } else if (token.getImagem().equalsIgnoreCase("alter")) {
            no.addFilho(AlterStm());
        } else {
            erros.add("Esperado na Query: 'alter ou create' Linha: " + token.getLinha());
        }
        return no;
    }

    // <Alter Stm> ::= alter table Id add <Constraint>
    No AlterStm() {
        No no = new No(TipoNo.No_AlterStm);
        if (token.getImagem().equalsIgnoreCase("alter")) {
            no.addFilho(new No(TipoNo.No_Token, token));
            nextToken();
            if (token.getImagem().equalsIgnoreCase("table")) {
                no.addFilho(new No(TipoNo.No_Token, token));
                nextToken();
                if (token.getClasse().equalsIgnoreCase("ID")) {
                    no.addFilho(new No(TipoNo.No_Token, token));
                    nextToken();
                    if (token.getImagem().equalsIgnoreCase("add")) {
                        no.addFilho(new No(TipoNo.No_Token, token));
                        nextToken();
                        no.addFilho(Constraint());
                    } else {
                        erros.add("Esperado na AlterStm: 'add' Linha: " + token.getLinha());
                    }
                } else {
                    erros.add("Esperado na AlterStm: 'ID' Linha: " + token.getLinha());
                }
            } else {
                erros.add("Esperado na AlterStm: 'table' Linha: " + token.getLinha());
            }
        } else {
            erros.add("Esperado na AlterStm: 'alter' Linha: " + token.getLinha());
        }
        return no;
    }

    //<CreateStm>  ::= create table Id '(' <Field Def List> ')' ';'
    No CreateStm() {
        No no = new No(TipoNo.No_CreateStm);
        if (token.getImagem().equalsIgnoreCase("create")) {
            no.addFilho(new No(TipoNo.No_Token, token));
            nextToken();
            if (token.getImagem().equalsIgnoreCase("table")) {
                no.addFilho(new No(TipoNo.No_Token, token));
                nextToken();
                if (token.getClasse().equalsIgnoreCase("ID")) {
                    no.addFilho(new No(TipoNo.No_Token, token));
                    nextToken();
                    if (token.getImagem().equals("(")) {
                        no.addFilho(new No(TipoNo.No_Token, token));
                        nextToken();
                        no.addFilho(FieldDefList());
                        if (token.getImagem().equals(")")) {
                            no.addFilho(new No(TipoNo.No_Token, token));
                            nextToken();
                        } else {
                            erros.add("Esperado na CreateStm: ')' Linha: " + token.getLinha());
                        }
                    } else {
                        erros.add("Esperado na CreateStm: '(' Linha: " + token.getLinha());
                    }
                } else {
                    erros.add("Esperado na CreateStm: 'ID' Linha: " + token.getLinha());
                }
            } else {
                erros.add("Esperado na CreateStm: 'table' Linha: " + token.getLinha());
            }
        } else {
            erros.add("Esperado na CreateStm: 'create' Linha: " + token.getLinha());
        }
        return no;
    }

    //<FieldDefList> ::= <FieldDef> <FieldDefList2>
    No FieldDefList() {
        No no = new No(TipoNo.No_FieldDefList);
        no.addFilho(FieldDef());
        no.addFilho(FieldDefList2());
        return no;
    }

    //<FieldDefList2> ::= ',' <Field Def> <Field Def List 2> | <ConstraintList>
    No FieldDefList2() {
        No no = new No(TipoNo.No_FieldDefList2);
        if (token.getImagem().equals(",")) {
            Token lookAhead = lookAhead();
            if (lookAhead.getClasse().equalsIgnoreCase("ID")) {
                no.addFilho(new No(TipoNo.No_Token, token));
                nextToken();
                no.addFilho(FieldDef());
                no.addFilho(FieldDefList2());
            } else {
                no.addFilho(ConstraintList());
            }
        }
        return no;
    }

    //<FieldDef>   ::= Id <Type> <Not Null>
    No FieldDef() {
        No no = new No(TipoNo.No_FieldDef);
        if (token.getClasse().equalsIgnoreCase("ID")) {
            no.addFilho(new No(TipoNo.No_Token, token));
            nextToken();
            no.addFilho(Type());
            no.addFilho(NotNull());
        }
        return no;
    }

    //<Not Null> ::= not null |
    No NotNull() {
        No no = new No(TipoNo.No_NotNull);
        if (token.getImagem().equalsIgnoreCase("not")) {
            no.addFilho(new No(TipoNo.No_Token, token));
            nextToken();
            if (token.getImagem().equalsIgnoreCase("null")) {
                no.addFilho(new No(TipoNo.No_Token, token));
                nextToken();
            } else {
                erros.add("Esperado na NotNull: 'null' Linha: " + token.getLinha());
            }
        }
        return no;
    }

    //<Type>  ::=  bit  | date  |  time | timestamp |  decimal  |  real |  float
    //|  smallint |  integer | int  |  interval |  character |  varchar <Tamanho>
    No Type() {
        No no = new No(TipoNo.No_Type);
        if (token.getClasse().equalsIgnoreCase("PR")) {
            if ((token.getImagem().equalsIgnoreCase("varchar")) || token.getImagem().equalsIgnoreCase("varchar2")) {
                no.addFilho(new No(TipoNo.No_Token, token));
                nextToken();
                no.addFilho(Tamanho());
            } else {
                no.addFilho(new No(TipoNo.No_Token, token));
                nextToken();
            }
        } else {
            erros.add("Esperado na Tipo: 'PR' Linha: " + token.getLinha());
        }
        return no;
    }

    //<Tamanho> ::= '(' IntegerLiteral ')'
    No Tamanho() {
        No no = new No(TipoNo.No_Tamanho);
        if (token.getImagem().equals("(")) {
            no.addFilho(new No(TipoNo.No_Token, token));
            nextToken();
            if (token.getClasse().equalsIgnoreCase("CLI")) {
                no.addFilho(new No(TipoNo.No_Token, token));
                nextToken();
                if (token.getImagem().equals(")")) {
                    no.addFilho(new No(TipoNo.No_Token, token));
                    nextToken();
                } else {
                    erros.add("Esperado na Tamanho: ')' Linha: " + token.getLinha());
                }
            } else {
                erros.add("Esperado na Tamanho: 'CLI' Linha: " + token.getLinha());
            }
        } else {
            erros.add("Esperado na Tamanho: '(' Linha: " + token.getLinha());
        }
        return no;
    }

    //<ConstraintList> ::= ',' <ConstraintType> <ConstraintList> |
    No ConstraintList() {
        No no = new No(TipoNo.No_ConstraintList);
        if (token.getImagem().equalsIgnoreCase(",")) {
            no.addFilho(new No(TipoNo.No_Token, token));
            nextToken();
            no.addFilho(ConstraintType());
            no.addFilho(ConstraintList());
        }
        return no;
    }

    //<Constraint> ::= constraint Id <Constraint Type>  | <Constraint Type>
    No Constraint() {
        No no = new No(TipoNo.No_Constraint);
        if (token.getImagem().equalsIgnoreCase("constraint")) {
            no.addFilho(new No(TipoNo.No_Token, token));
            nextToken();
            if (token.getClasse().equalsIgnoreCase("ID")) {
                no.addFilho(new No(TipoNo.No_Token, token));
                nextToken();
                no.addFilho(ConstraintType());
            }
        } else {
            no.addFilho(ConstraintType());
        }
        return no;
    }

    //<Constraint Type> ::= primary key '(' <Id List> ')'
    //| foreign key '(' <Id List> ')' references Id '(' <Id List> ')'
    No ConstraintType() {
        No no = new No(TipoNo.No_ConstraintType);
        if (token.getImagem().equalsIgnoreCase("primary")) {
            no.addFilho(new No(TipoNo.No_Token, token));
            nextToken();
            if (token.getImagem().equalsIgnoreCase("key")) {
                no.addFilho(new No(TipoNo.No_Token, token));
                nextToken();
                if (token.getImagem().equals("(")) {
                    no.addFilho(new No(TipoNo.No_Token, token));
                    nextToken();
                    no.addFilho(IdList());
                    if (token.getImagem().equals(")")) {
                        no.addFilho(new No(TipoNo.No_Token, token));
                        nextToken();
                    } else {
                        erros.add("Esperado na ConstraintType: ')' Linha: " + token.getLinha());
                    }
                } else {
                    erros.add("Esperado na ConstraintType: '(' Linha: " + token.getLinha());
                }
            } else {
                erros.add("Esperado na ConstraintType: 'key' Linha: " + token.getLinha());
            }
        } else { //| foreign key '(' <Id List> ')' references Id '(' <Id List> ')'
            if (token.getImagem().equalsIgnoreCase("foreign")) {
                no.addFilho(new No(TipoNo.No_Token, token));
                nextToken();
                if (token.getImagem().equalsIgnoreCase("key")) {
                    no.addFilho(new No(TipoNo.No_Token, token));
                    nextToken();
                    if (token.getImagem().equals("(")) {
                        no.addFilho(new No(TipoNo.No_Token, token));
                        nextToken();
                        no.addFilho(IdList());
                        if (token.getImagem().equals(")")) {
                            no.addFilho(new No(TipoNo.No_Token, token));
                            nextToken();
                            if (token.getImagem().equalsIgnoreCase("references")) {
                                no.addFilho(new No(TipoNo.No_Token, token));
                                nextToken();
                                if (token.getClasse().equalsIgnoreCase("ID")) {
                                    no.addFilho(new No(TipoNo.No_Token, token));
                                    nextToken();
                                    if (token.getImagem().equals("(")) {
                                        no.addFilho(new No(TipoNo.No_Token, token));
                                        nextToken();
                                        no.addFilho(IdList());
                                        if (token.getImagem().equals(")")) {
                                            no.addFilho(new No(TipoNo.No_Token, token));
                                            nextToken();
                                        } else {
                                            erros.add("Esperado na ConstraintType: ')' Linha: " + token.getLinha());
                                        }
                                    } else {
                                        erros.add("Esperado na ConstraintType: '(' Linha: " + token.getLinha());
                                    }
                                } else {
                                    erros.add("Esperado na ConstraintType: 'ID' Linha: " + token.getLinha());
                                }
                            } else {
                                erros.add("Esperado na ConstraintType: 'references' Linha: " + token.getLinha());
                            }
                        } else {
                            erros.add("Esperado na ConstraintType: ')' Linha: " + token.getLinha());
                        }
                    } else {
                        erros.add("Esperado na ConstraintType: '(' Linha: " + token.getLinha());
                    }
                }
            }
        }
        return no;
    }

    //<Id List> ::= Id <Id List 2>
    No IdList() {
        No no = new No(TipoNo.No_IdList);
        if (token.getClasse().equalsIgnoreCase("ID")) {
            no.addFilho(new No(TipoNo.No_Token, token));
            nextToken();
            no.addFilho(IdList2());
        } else {
            erros.add("Esperado na IdList: 'ID' Linha: " + token.getLinha());
        }
        return no;
    }

    //<Id List 2> ::= ',' Id <Id List 2> |
    No IdList2() {
        No no = new No(TipoNo.No_IdList2);
        if (token.getClasse().equals(",")) {
            no.addFilho(new No(TipoNo.No_Token, token));
            nextToken();
            if (token.getClasse().equalsIgnoreCase("ID")) {
                no.addFilho(new No(TipoNo.No_Token, token));
                nextToken();
                no.addFilho(IdList2());
            }
        }
        return no;
    }

    public boolean temErros() {
        return erros.size() > 0;
    }

    public void mostraErros() {
        for (String erro : erros) {
            System.out.println(erro);
        }
    }

    public void mostraArvore() {
        mostraNo(raiz, "     ");
    }

    private void mostraNo(No no, String indentacao) {
        if (no != null) {
            if (no.getTipo() == TipoNo.No_Token) {
                System.out.println(indentacao + no.getToken().getImagem());
            } else {
                System.out.println(indentacao + no.getTipo());
            }

            for (No filho : no.getFilhos()) {
                mostraNo(filho, indentacao + "     ");
            }
        }
    }

    private Token lookAhead() {
        return tokens.get(pToken);
    }
}
